AWS IoT Eventsを使って、LambdaやDynamoDBを使わずにハートビート監視をやってみた
IoTの仕事に携わっていると、ハートビート監視が話題にあがります。 具体的には、デバイスが定期的に発信する信号をクラウドで監視し、一定期間内に受信できなければ「デバイスとの接続が切れています」的な内容をメールで送信します。
この仕組を実現するとき、DynamoDBに状態を保存して、一定時間毎に起動するLambdaで確認していたのですが、IoT Eventsを使えば楽にできそうなので試してみました。
なお、CloudFormationで試した版はこちらです。
おすすめの方
- IoT Eventsの雰囲気を知りたい方
- ハートビート監視の仕組みを検討中の方
IoT Eventsとは
下記をご覧ください。
構築するハートビートの要件
ざっくりと下記です。リセットしないウォッチドッグタイマーですね。
- デバイス
- 2分毎にハートビート用の信号をクラウドに発信する
- クラウド
- デバイスからハートビート用の信号が2回連続で来なかったとき、デバイス切断と判断してメールを送信する
- デバイス切断状態でハートビート用の信号が届いたとき、デバイス接続が復旧と判断してメールを送信する
ハートビート用のトピックは下記とします。
test/<デバイスID>/heartbeat
デバイスが送信するハートビート用のデータは下記とします。タイムスタンプ(ミリ秒)です。
{ "timestamp": 1595817156346 }
SNSトピックを作成する
デバイス切断時と接続復旧時にIoT Evetnsが発行するSNSトピックを作成します。
- トピック名: heartbeat-send-mail-topic
ここでEメールのサブスクリプション設定を行います。
件名や本文にこだわる場合は、LambdaとSESを経由して送信すると良いですね。
IoT Eventsで探知機モデルを作成する
最初に仕組みを解説
デバイス毎にあらかじめ5分のタイマーをセットしておき、ハートビート用の信号が来たらタイマーを5分にリセットします。 もしハートビート用の信号が来なければ、タイマーがゼロになる5分後にSNSトピックを発行してメールを送信します。 タイマーの5分は4分(2分毎の信号が2回)と1分(バッファ)を足した値です。
探知機モデルの作成
IoT Eventsにアクセスし、探知機モデルの作成を選択します。
テンプレートでも作成できますが、ここでは新しく作成します。
なにかできました。これを作り込んでいきます。
入力の設定
右上の「入力の作成」を選択して設定します。
- 入力名: TestHeartbeatInputData
次のJSONファイルを作成してアップロードします。これは、デバイスが送信するデータをIoTルールアクションで整形したJSONデータです。
{ "deviceId": "d1234", "payload": { "timestamp": 1595817156346 } }
入力属性はすべて選択しておきます(すべて使います)。
online状態の作成
既存のState_1
を選択し、状態名をonline
に変更します。
OnEnterイベントの設定
OnEnter
のイベント追加を選択し、5分間のタイマーを設定します。
- イベント名: Init_timer
- 条件: true
- アクション: タイマーの設定
- オペレーション: 作成
- タイマー名: heartbeat_timer
- 経過時間: 5分
OnInputイベントの設定
OnInput
のイベント追加を選択し、ハートビート用のデータを受け取ったときのタイマーをリセットを設定します。
- イベント名: Reset_timer
- 条件: $input.TestHeartbeatInputData.payload.timestamp > 0
- アクション: タイマーの設定
- オペレーション: リセット
- タイマー名: heartbeat_timer
offline状態の作成
「状態」をドラッグして新しい状態を追加し、状態名をoffline
に変更します。
OnEnterイベントの設定
OnEnter
のイベント追加を選択し、SNSトピックを発行してメール送信する設定を追加します。
- イベント名: Send_Email_to_offline
- 条件: true
- アクション: SNSメッセージの送信
- SNSトピック: heartbeat-send-mail-topic
- ペイロード :カスタムペイロード
- 種類: 文字列
- カスタムペイロード: 'デバイスの切断を検知しました。対象デバイスID: ${$input.TestHeartbeatInputData.deviceId}'
OnExitイベントの設定
OnExit
のイベント追加を選択し、SNSトピックを発行してメール送信する設定を追加します。
- イベント名: Send_Email_to_online
- 条件: true
- アクション: SNSメッセージの送信
- SNSトピック: heartbeat-send-mail-topic
- ペイロード :カスタムペイロード
- 種類: 文字列
- カスタムペイロード: 'デバイスの接続復旧を検知しました。対象デバイスID: ${$input.TestHeartbeatInputData.deviceId}'
状態遷移を定義する
onlineからofflineの遷移
online状態を選択すると表示される三角マークをドラッグしてofflineにつなぎます。
イベント名とトリガーを下記にします。これによって、5分タイマーがタイムアウトしたとき、offline状態に遷移します。
- イベント名: to_offline
- トリガー: timeout("heartbeat_timer")
offlineからonlineへの遷移
同様にofflineからonlineへの遷移を作成します。これによって、TestHeartbeatInputData
のデータ入力があったとき、デバイス復帰と判断してonline状態に遷移します。
- イベント名: to_online
- トリガー: currentInput("TestHeartbeatInputData")
探知機モデルを発行する
右上の「発行」ボタンを選択し、モデル名とIAMロール名を入力します。IAMロールはここで同時に新規作成してもらいます。
- モデル名: SampleHeartbeatModel
- IAMロール名: iot-events-sample-device-heartbeat-role (※新規作成)
- 探知器生成メソッド: 一意のキー値ごとに探知器を作成する
- 探知器作成キー: deviceId
- ディテクターの評価方法: バッチ評価
最後に「保存して発行する」ボタンを押せば完了です!
ログの有効化
IoT Eventsのログを有効化して、CloudWatch Logsで見れるようにしておきます。
IoTルールアクションの作成
IoT Coreでルールを作成します。
- ルールの名前: sample_iot_events_heartbeat_rule
- SQLルール: 下記
- アクション: IoT Events入力にメッセージを送信する
- 入力名: TestHeartbeatInputData
- ロール: sample-iot-events-heartbeat-rule-role (※新規作成)
SELECT topic(2) as deviceId * as payload FROM 'test/+/heartbeat'
動作確認してみる
ここまでで準備は整いました。実デバイスが存在する前提でモノや証明書の作成をしてもよいのですが、そこは省略してIoT Coreのテスト画面を使って、あたかもデバイスが存在するかのように振る舞うことにします。
1台目のデバイスでハートビートを送信する
下記の送信を行います。
- トピック:
test/d1234/heartbeat
- データ:下記
{ "timestamp": 1595910356921 }
探知機モデルの様子
ディテクターにデバイス(d1234)が追加されました。現在の状態はonlineですね。この状態で5分待ちます。
5分後にデバイス切断されたメールが届く
メールが届きました!
ディテクターの様子もofflineになっています。
再びハートビートを送信する
IoT Coreのテスト画面で再びハートビートを送信すると、接続復帰メールが来ました。
ディテクターの様子もonlineになりました。
2台目のデバイスでハートビートを送信する
下記の送信を行います。
- トピック:
test/d7777/heartbeat
- データ:下記
{ "timestamp": 1595911179367 }
ディテクターが2つに増えました。
1台目のデバイスのみハートビート送信し続ける
1台目のデバイス(d1234)のみ、ハートビート送信し続けます。5分程待つと、2台目のデバイスの切断メールが届きました。
CloudWatch Logsの様子
次のようなログが出力されていました。
{ "timestamp": "2020-07-28T04:27:41.441Z", "level": "INFO", "logMessage": "Initializing state to online", "context": "Transition", "status": "Success", "messageId": "e9f72b9c-e208-4c64-a66c-870e50a3fd29", "keyValue": "d1234", "detectorModelName": "SampleHeartbeatModel", "eventName": "onInitialize" }
{ "timestamp": "2020-07-28T04:32:41.535Z", "level": "INFO", "logMessage": "Changing state from online to offline", "context": "Transition", "status": "Success", "messageId": "ee10a77c-27a3-4197-b086-8a8ddb33ce52", "keyValue": "d1234", "detectorModelName": "SampleHeartbeatModel", "eventName": "to_offline" }
{ "timestamp": "2020-07-28T04:32:42.002Z", "level": "INFO", "logMessage": "SNS message successfully published. Topic ARN: arn:aws:sns:ap-northeast-1:1234567890:heartbeat-send-mail-topic", "context": "Action", "status": "Success", "keyValue": "d1234", "detectorModelName": "SampleHeartbeatModel", "actionExecutionId": "016be2b6-fe70-364b-8b20-42e1b3b083ce", "actionType": "SNSTopicPublish", "eventName": "Send_Email_to_offline" }
{ "timestamp": "2020-07-28T04:38:13.331Z", "level": "INFO", "logMessage": "Changing state from offline to online", "context": "Transition", "status": "Success", "messageId": "50236093-c862-4178-bf21-1195123936a0", "keyValue": "d1234", "detectorModelName": "SampleHeartbeatModel", "eventName": "to_online" }
{ "timestamp": "2020-07-28T04:38:13.797Z", "level": "INFO", "logMessage": "SNS message successfully published. Topic ARN: arn:aws:sns:ap-northeast-1:1234567890:heartbeat-send-mail-topic", "context": "Action", "status": "Success", "messageId": "50236093-c862-4178-bf21-1195123936a0", "keyValue": "d1234", "detectorModelName": "SampleHeartbeatModel", "actionExecutionId": "b2bea181-9834-3484-ade6-f828fe3c838a", "actionType": "SNSTopicPublish", "eventName": "Send_Email_to_online" }
さいごに
IoT Eventsを使うことによって、DynamoDBの状態管理テーブルや処理を行うLambgaを使わずにハートビート監視を実現しました。 IoTではハートビート監視は切っても切れない関係だと思うので、IoT Eventsを使って楽に実現できそうです。